/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.umeox.watch.moto.launcher.base;

import static android.content.Intent.ACTION_BATTERY_CHANGED;
import static com.umeox.moto.common.utils.NetworkUtil.NetworkType.WIFI;
import static com.umeox.watch.moto.launcher.fragment.QuickSettingsFragment.ACTION_SIM_STATE_CHANGED;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

import com.umeox.moto.common.log.Logger;
import com.umeox.moto.common.utils.NetworkUtil;
import com.umeox.watch.moto.launcher.event.DeviceStateChangedListener;
import com.umeox.watch.moto.pedometer.base.BaseApp;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;


public class DeviceStatusManager {
    private static final String TAG = "DeviceStatusManager";

    private final TelephonyManager mTelephonyManager;


    private ServiceState mServiceState;

    private int mCindService = DeviceStatusConstants.NETWORK_STATE_NOT_AVAILABLE;

    private int mNumActive;
    // Current Call Setup State
    private int mCallState = DeviceStatusConstants.CALL_STATE_IDLE;
    // Number of held (background) calls
    private int mNumHeld;
    // HFP 1.6 CIND signal value
    private int mCindSignal;
    // HFP 1.6 CIND roam value
    private int mCindRoam = DeviceStatusConstants.SERVICE_TYPE_HOME;
    // HFP 1.6 CIND battchg value
    private int mCindBatteryLevel;

    private final HashMap<Object, Integer> mDeviceEventMap = new HashMap<>();

    private final List<DeviceStateChangedListener> mListeners = new ArrayList<>();
    private PhoneStateListener mPhoneStateListener;
    private int mSimState;
    private final SimStateBroadcastReceiver mSimStateReceiver;
    private final Context mContext;
    private int mCindBatteryStatus;
    private String mCindNetworkType = NetworkUtil.NetworkType.NONE;
    private WifiManager mWifiManager;
    private int mCindWifiSignalLevel;

    public void addDeviceStateChangedListener(DeviceStateChangedListener listener) {
        if (listener == null) {
            return;
        }
        if (mListeners.contains(listener)) {
            return;
        }
        mListeners.add(listener);
        mCindNetworkType = NetworkUtil.getCurrentNetworkType(mContext);
        startListenForPhoneState();
        sendBatteryStatusChange();
        sendDeviceStateChanged();
        sendNetworkTypeChange();
        if (mCindNetworkType.equals(WIFI))
            sendWifiSignalLevelChange();
    }

    public void removeDeviceStateChangedListener(DeviceStateChangedListener listener) {
        mListeners.remove(listener);
        stopListenForPhoneState();
    }

    private DeviceStatusManager() {

        mContext = BaseApp.getAppContext();
        mTelephonyManager =
                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        listenForPhoneState(this, PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
        mSimStateReceiver = new SimStateBroadcastReceiver();
        final IntentFilter filter = new IntentFilter(ACTION_SIM_STATE_CHANGED);
        filter.addAction(ACTION_BATTERY_CHANGED);
        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        mContext.registerReceiver(mSimStateReceiver, filter);
    }

    /**
     * Cleanup this instance. Instance can no longer be used after calling this method.
     */
    public void cleanup() {
        synchronized (mDeviceEventMap) {
            mDeviceEventMap.clear();
            stopListenForPhoneState();
            mContext.unregisterReceiver(mSimStateReceiver);
        }

    }

    @Override
    public String toString() {
        return "DeviceStatus [mTelephonyServiceAvailability=" + mCindService + ", mNumActive="
                + mNumActive + ", mCallState=" + mCallState + ", mNumHeld=" + mNumHeld
                + ", mSignal=" + mCindSignal + ", mRoam=" + mCindRoam + ", mBatteryCharge="
                + mCindBatteryLevel + ", TelephonyEvents=" + getTelephonyEventsToListen() + "]";
    }

    private int getTelephonyEventsToListen() {
        synchronized (mDeviceEventMap) {
            int stateEvent = PhoneStateListener.LISTEN_NONE;
            for (Integer value : mDeviceEventMap.values()) {
                stateEvent = stateEvent | value;
            }
            return stateEvent;
        }
    }

    public static DeviceStatusManager getInstance() {
        return InstanceHolder.instance;
    }

    private static class InstanceHolder {
        @SuppressLint("StaticFieldLeak")
        private static final DeviceStatusManager instance = new DeviceStatusManager();
    }

    /**
     * Start or stop listening for phone state change
     *
     * @param device remote device that subscribes to this phone state update
     * @param events events in {@link PhoneStateListener} to listen to
     */
    public void listenForPhoneState(Object device, int events) {
        synchronized (mDeviceEventMap) {
            int prevEvents = getTelephonyEventsToListen();
            if (events == PhoneStateListener.LISTEN_NONE) {
                mDeviceEventMap.remove(device);
            } else {
                mDeviceEventMap.put(device, events);
            }
            int updatedEvents = getTelephonyEventsToListen();
            if (prevEvents != updatedEvents) {
                stopListenForPhoneState();
                startListenForPhoneState();
            }
        }
    }

    private void startListenForPhoneState() {
        if (mPhoneStateListener != null) {
            Log.w(TAG, "startListenForPhoneState, already listening");
            return;
        }
        int events = getTelephonyEventsToListen();
        if (events == PhoneStateListener.LISTEN_NONE) {
            Log.w(TAG, "startListenForPhoneState, no event to listen");
            return;
        }

        mPhoneStateListener = new DeviceStatusPhoneStateListener();
        mTelephonyManager.listen(mPhoneStateListener, events);
    }

    private void stopListenForPhoneState() {
        if (mPhoneStateListener == null) {
            Log.i(TAG, "stopListenForPhoneState(), no listener indicates nothing is listening");
            return;
        }
        Log.i(TAG, "stopListenForPhoneState(), stopping listener, enabled_events="
                + getTelephonyEventsToListen());
        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
        mPhoneStateListener = null;
    }

    int getCindService() {
        return mCindService;
    }

    int getNumActiveCall() {
        return mNumActive;
    }


    public void setNumActiveCall(int numActive) {
        mNumActive = numActive;
    }

    int getCallState() {
        return mCallState;
    }

    public void setCallState(int callState) {
        mCallState = callState;
    }

    int getNumHeldCall() {
        return mNumHeld;
    }


    public void setNumHeldCall(int numHeldCall) {
        mNumHeld = numHeldCall;
    }

    ServiceState getServiceState() {
        return mServiceState;
    }

    int getCindSignal() {
        return mCindSignal;
    }

    int getCindRoam() {
        return mCindRoam;
    }


    int getCindBatteryLevel() {
        return mCindBatteryLevel;
    }

    boolean isInCall() {
        return (mNumActive >= 1);
    }


    private class SimStateBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action == null) {
                return;
            }
            switch (action) {
                case ACTION_SIM_STATE_CHANGED:
                    synchronized (mDeviceEventMap) {
                        mSimState = mTelephonyManager.getSimState();
                        Log.d(TAG, "onReceive: mSimState:" + mSimState);
                        if (mSimState != TelephonyManager.SIM_STATE_READY) {
                            mServiceState = null;
                            mSimState = DeviceStatusConstants.SIM_STATE_NOT_AVAILABLE;
                            sendDeviceStateChanged();
                        }
                        stopListenForPhoneState();
                        startListenForPhoneState();
                    }
                    break;
                case ACTION_BATTERY_CHANGED:
                    int batteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
                    int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
                    Log.d(TAG, "onReceive mCindBatteryLevel" + mCindBatteryLevel + " BatteryLevel:" + batteryLevel + " Status:" + mCindBatteryStatus + " current:" + batteryStatus);
                    if (batteryLevel < 0) {
                        return;
                    }
                    if (mCindBatteryLevel != batteryLevel || mCindBatteryStatus != batteryStatus) {
                        mCindBatteryLevel = batteryLevel;
                        mCindBatteryStatus = batteryStatus;
                        sendBatteryStatusChange();
                    }

                    break;

                case ConnectivityManager.CONNECTIVITY_ACTION:
                    final String currentNetworkType = NetworkUtil.getCurrentNetworkType(context);
                    Log.d(TAG, "onReceive CindNetworkType:" + mCindNetworkType + " currentNetworkType;" + currentNetworkType);
//                    if(NetUtils.isConnect2Wifi()){
//                        sendWifiSignalLevelChange();
//                    }
                    if (TextUtils.equals(mCindNetworkType, currentNetworkType)) {
                        return;
                    }
                    mCindNetworkType = currentNetworkType;
                    sendNetworkTypeChange();
                    break;

                case WifiManager.RSSI_CHANGED_ACTION:
                    final int wifiSignalLevel = getWifiSignalLevel();
                    Log.i(TAG, "onReceive CindWifiSignalLevel:" + mCindNetworkType + " currentWifiSignalLevel:" + wifiSignalLevel);
                    if (mCindWifiSignalLevel == wifiSignalLevel) {
                        return;
                    }
                    mCindWifiSignalLevel = wifiSignalLevel;
                    sendWifiSignalLevelChange();
                    break;
            }
        }


    }

    private synchronized void sendBatteryStatusChange() {
        for (DeviceStateChangedListener stateChangedListener : mListeners) {
            stateChangedListener.onBatteryStatusChange(mCindBatteryStatus, mCindBatteryLevel);
        }
        Logger.d(TAG, "sendBatteryStatusChange: " + mCindBatteryStatus + " level:" + mCindBatteryLevel);
    }

    private synchronized void sendNetworkTypeChange() {
        for (DeviceStateChangedListener stateChangedListener : mListeners) {
            stateChangedListener.onNetworkTypeChange(mCindNetworkType);
        }
        Logger.d(TAG, "sendNetworkTypeChange: " + mCindNetworkType);
    }

    private synchronized void sendWifiSignalLevelChange() {
        for (DeviceStateChangedListener stateChangedListener : mListeners) {
            stateChangedListener.onWifiSignalLevelChange(mCindWifiSignalLevel);
        }
        Logger.d(TAG, "sendWifiSignalLevelChange: " + mCindWifiSignalLevel);
    }


    private synchronized void sendDeviceStateChanged() {

        int signal = mCindService == DeviceStatusConstants.NETWORK_STATE_AVAILABLE ? mCindSignal : 0;

        if (DeviceStatusConstants.SIM_STATE_NOT_AVAILABLE == mSimState) {
            signal = -1;
        }

        for (DeviceStateChangedListener stateChangedListener : mListeners) {
            stateChangedListener.onSignalStatusChange(signal);
        }

        Log.d(TAG, "sendDeviceStateChanged. mService=" + mCindService
                + " mSignal=" + mCindSignal + " mRoam=" + mCindRoam
                + " mBatteryCharge=" + mCindBatteryLevel);

    }


    private class DeviceStatusPhoneStateListener extends PhoneStateListener {

        @Override
        public synchronized void onServiceStateChanged(ServiceState serviceState) {
            mServiceState = serviceState;
            int cindService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE)
                    ? DeviceStatusConstants.NETWORK_STATE_AVAILABLE
                    : DeviceStatusConstants.NETWORK_STATE_NOT_AVAILABLE;
            Logger.d(TAG, "onServiceStateChanged:" + cindService + " mCindService:" + mCindService);
            if (cindService == mCindService) {

                return;
            }
            mCindService = cindService;
            sendDeviceStateChanged();
        }

        @Override
        public void onSignalStrengthsChanged(SignalStrength signalStrength) {

            int prevSignal = mCindSignal;

            if (mCindService == DeviceStatusConstants.NETWORK_STATE_NOT_AVAILABLE) {
                mCindSignal = 0;
            } else {
                mCindSignal = getSignalStrengthsLevel(signalStrength);
            }
            Log.d(TAG, "onSignalStrengthsChanged: " + prevSignal + " CindSignal:" + mCindSignal);

            mCindSignal = Math.max(Math.min(mCindSignal, 4), 0);

            if (prevSignal != mCindSignal) {
                sendDeviceStateChanged();
            }
        }
    }


    /**
     * 获取wifi信号等级
     *
     * @return
     */
    public int getWifiSignalLevel() {
        if (mWifiManager == null) {
            mWifiManager = (WifiManager) mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        }
        WifiInfo mWifiInfo = mWifiManager.getConnectionInfo();
        return WifiManager.calculateSignalLevel(mWifiInfo.getRssi(), 5);
    }

    private int getSignalStrengthsLevel(SignalStrength signalStrength) {
        int level = -1;
        try {
            Method levelMethod = SignalStrength.class.getDeclaredMethod("getLevel");
            level = (int) levelMethod.invoke(signalStrength);
        } catch (Exception e) {
            Log.e("ClockView", "getSignalStrengthsLevel: ", e);
        }
        return level;
    }
}
